package com.vonzhou.springinaction.springidol;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * @author XiaoDongLei
 * @Package com.vonzhou.springinaction.springidol
 * @date 2018/3/27
 * @Description: 在JavaConfig中启用AspectJ注解的自动代理
 * 1、通过@Pointcut注解声明频繁使用的切点表达式。如果不这样做的话，你需要在这些地方使用那个更长的切点表达式。我们现在把所有通知注解中的长表达式都替换成了performance()。
 * performance()方法的实际内容并不重要，在这里它实际上应该是空的。其实该方法本身只是一个标识，供@Pointcut注解依附。
 *
 * 2、术语的关键词：
 * 通知和切点是切面的最基本元素。切面是通知和切点的结合————通知和切点共同定义了切面的全部内容——它是什么，在何时(通知Advice)和何处(切点Pointcut)完成其功能。
 *
 * Spring借助AspectJ的切点表达式语言来定义Spring切面。通过切点来选择连接点。
 * execution()：用于匹配是连接点的执行方法。
 * 连接点：是在应用执行过程中能够插入切面的一个点。
 *
 *
 * Spring只支持方法级别的连接点。因为Spring基于动态代理，所以Spring只支持方法连接点。这与一些其他的AOP框架是不同的，例如AspectJ和JBoss，除了方法切点，它们还提供了字段和构造器接入点。Spring缺少对字段连接点的支持，无法让我们创建细粒度的通知，例如拦截对象字段的修改。而且它不支持构造器连接点，我们就无法在bean创建时应用通知。
 */
@Aspect
public class Audience {
	@Pointcut("execution(* com.vonzhou.springinaction.springidol.Performer.perform(..))")
	public void performance() {
	}

	@Before("performance()")
	public void takeSeats() {
		System.out.println("The audience is taking their seats.");
	}

	@Before("performance()")
	public void turnOffCellPhones() {
		System.out.println("The audience is turning off their cellphones");
	}

	@AfterReturning("performance()")
	public void applaud() {
		System.out.println("CLAP CLAP CLAP CLAP CLAP");
	}

	@AfterThrowing("performance()")
	public void demandRefund() {
		System.out.println("Boo! We want our money back!");
	}

	/**
	 *
	 * @param proceedingJoinPoint
	 */

	/**
	 * description: ProceedingJoinPoint这个对象是必须要有的，因为你要在通知中通过它来调用被通知的方法。通知方法中可以做任何的事情，当要将控制权交给被通知的方法时，它需要调用ProceedingJoinPoint的proceed()方法。
	 *
	 * 如果不调这个方法的话，那么你的通知实际上会阻塞对被通知方法的调用。有可能这就是你想要的效果，但更多的情况是你希望在某个点上执行被通知的方法。
	 * 有意思的是，你可以不调用proceed()方法，从而阻塞对被通知方法的访问，与之类似，你也可以在通知中对它进行多次调用。要这样做的一个场景就是实现重试逻辑，也就是在被通知方法失败后，进行重复尝试。
	 * @author leixiaodong1993@163.com
	 * @date 2018/3/27 10:09
	 * @param proceedingJoinPoint
	 * @return void
	 */
	@Around("performance()")
	public void around(ProceedingJoinPoint proceedingJoinPoint) {
		System.out.println("before around ====");
		try {
			proceedingJoinPoint.proceed();
			System.out.println("after around ===");
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
	}
}